#include <vector>

#include <stdlib.h>

#include "gsupplyLinkSolution.h"

using namespace std ;

GSupplyLinkProblem* globalProblem ;




/**
 *
 */
GSupplyLinkSolution::GSupplyLinkSolution():problem(globalProblem),evaluation(-1){
    if (globalProblem!=NULL && globalProblem->getNP()>0)
        processingSchedule.resize(globalProblem->getNP()) ;    

    initValues() ;
}




/**
 *
 */
GSupplyLinkSolution::GSupplyLinkSolution(GSupplyLinkProblem& pb_):problem(&pb_),processingSchedule(pb_.getNP()),evaluation(-1){
    globalProblem = problem ;
    initValues() ;
}





/**
 *
 */
void GSupplyLinkSolution::initValues() {
  np = problem->getNP() ;
  tabBatchInformation.resize(np) ;
  tabBatchInformationRank.resize(np) ;

  resetConstraints() ;

  // Find lastDueDate :
  lastDueDate = -1 ;
  for (int j=0;j<np;j++)
    if (problem->getJobDueDate(j)>lastDueDate)
      lastDueDate = problem->getJobDueDate(j) ;
}




/**
 *
 */
double GSupplyLinkSolution::evaluate() {

    evaluation = -1 ;

    if (!this->checkConstraints()) return -1 ;

    evaluationTransporter = nbrBatch * problem->getTransporter(0).getDeliveryCost() ;

    evaluationCustomer = 0 ;
    for (int i=0;i<np;++i) {
        double jobcustcost = problem->getJobHoldingCost(i) ;
        double jobduedate = problem->getJobDueDate(i) ;
        double jobcomptime =  processingSchedule[i].getDeliveryCompletionTime() ;
        evaluationCustomer += jobcustcost*(jobduedate-jobcomptime) ;
    }
    evaluation = evaluationTransporter + evaluationCustomer ;

    return evaluation;
}






/**
 * checks if the solution satisfies the constraints
 *
 * @return true if the constraints are satisfied
 */
void GSupplyLinkSolution::resetConstraints() {
  for (int b=0;b<np;++b) {
      tabBatchInformation[b].completionTime = lastDueDate ;
      tabBatchInformation[b].size = 0.0 ;
      tabBatchInformation[b].nbrJob = 0 ;
      tabBatchInformationRank[b]=b ;
    }

}




/**
 *
 */
void GSupplyLinkSolution::goutBatchOrder(const char* st) {
  cout << "gout-" << st << ": " ;
  cout << "  Size in batches=[" ;
  for (int b=0;b<nbrBatch;++b) {
    cout << tabBatchInformation[b].size ;
    if (b<nbrBatch-1) cout << " ; " ; else cout << "] " ;
  }
  cout << " ; Nbr jobs in batches=[" ;
  for (int b=0;b<nbrBatch;++b) {
    cout << tabBatchInformation[b].nbrJob ;
    if (b<nbrBatch-1) cout << " ; " ; else cout << "] " ;
  }
  cout << "(decreasing:" ;
  for (int b=0;b<np;++b) {
    cout << tabBatchInformation[tabBatchInformationRank[b]].nbrJob ;
    if (b<np-1) cout << " ; " ; else cout << ")" << endl ;
  }
}



/**
 * checks if the solution satisfies the constraints from jobStart to jobEnd
 *
 * @return true if the constraints are satisfied
 */
bool GSupplyLinkSolution::checkConstraints(int jobStart, int jobEnd) {
    //get the number of batches ;
    // int nbrbatch = 1 ;
    // for(int i=0; i<np; i++){
    //     if (nbrbatch<processingSchedule[i].getBatchIndice()){
    //         nbrbatch = processingSchedule[i].getBatchIndice() ;
    //     }
    // }
    // this->nbrBatch = nbrbatch ;

    assert (this->nbrBatch>0) ;

    resetConstraints() ;

    for (int i=jobStart;i<=jobEnd;++i) {
        int batchIndice = processingSchedule[i].getBatchIndice() - 1 ;

        assert (batchIndice>=0 & batchIndice<this->nbrBatch) ;

        // Constraint (3) : Add the size of job i to the batch where job is is assigned
        tabBatchInformation[batchIndice].size += problem->getJobSize(i) ;
	tabBatchInformation[batchIndice].nbrJob++ ;

        // Constraint (2) : Update the completion time of the batch if the due date of the job is less
        if (tabBatchInformation[batchIndice].completionTime > problem->getJobDueDate(i)) {
            tabBatchInformation[batchIndice].completionTime = problem->getJobDueDate(i) ;
        }
    }

    if (problem->getProblemClass() == BP_LSDSP_CLASS_A) { // Sort according to increasing order of the number of jobs
      for (int i1=0;i1<jobEnd;++i1) {
	int imaxNbrJob = i1 ;
	int b1 = tabBatchInformationRank[i1] ;
	int maxNbrJob = tabBatchInformation[b1].nbrJob ;
	
	for (int i2=i1+1;i2<=jobEnd;++i2) {
	  int b2 = tabBatchInformationRank[i2] ;
	  if (maxNbrJob<tabBatchInformation[b2].nbrJob) {
	    maxNbrJob = tabBatchInformation[b2].nbrJob ;
	    imaxNbrJob = i2 ;
	  }
	}

	if (i1<imaxNbrJob) {
	  int temp = tabBatchInformationRank[i1]  ;
	  tabBatchInformationRank[i1] = tabBatchInformationRank[imaxNbrJob] ;
	  tabBatchInformationRank[imaxNbrJob] = temp ;
	}
      }
    }

    double lastDeliveryDate = PLUS_INFINI ;
    double delivtime = problem->getTransporter(0).getDeliveryTime() ;
    for (int rank=0;rank<nbrBatch;++rank) {
        // Constraint (3) : capacity constraint check
	int b = tabBatchInformationRank[rank] ;
        assert (tabBatchInformation[b].size <= problem->getTransporter(0).getCapacity()) ;

        // Constraint (5) : the interval between two successive delivery should be greater or equal to the delivery time
	double prevdelivdate = lastDeliveryDate - delivtime ;
        if (tabBatchInformation[b].completionTime > prevdelivdate) {
            tabBatchInformation[b].completionTime = prevdelivdate ;
        }
        lastDeliveryDate = tabBatchInformation[b].completionTime ;
    }

    // Constraint (4) : check if all th jobs in a batch have the same completion time as the batch
    for (int i=jobStart;i<=jobEnd;++i) {
        int batchnumber = processingSchedule[i].getBatchIndice()-1 ;

        // Constraint (2) : Update the completion time of the batch if the due date of the job is less
        if (tabBatchInformation[batchnumber].completionTime != processingSchedule[i].getDeliveryCompletionTime()) {
            processingSchedule[i].setDeliveryCompletionTime (tabBatchInformation[batchnumber].completionTime) ;
        }
    }

    return true;
}






/**
 *
 */
ostream& operator<< (ostream& o, const GSupplyLinkSolution& sol) {
    /*
        int nbrBatch;
        vector<GJobVariable> processingSchedule;
        double evaluation ;
        double evaluationTransporter;
        double evaluationCustomer;
    */
    int n = sol.np ;

    o << "GSupplyLinkSolution[[nbrBatch=" << sol.nbrBatch << ", evaluation=" << sol.evaluation ;
    o << ", evaluationCustomer=" << sol.evaluationCustomer << ", evaluationTransporter=" << sol.evaluationTransporter  ;

    // BATCH INDICES
    o << "\n  bi=[" ;
    for (int i=0;i<n;++i) {
        o << sol.processingSchedule[i].getBatchIndice() ;
        if (i<n-1) o << ", "; else o << "]" ;
    }
    o << "\n" ;

    // COMPLETION TIMES
    o << "  Ci=[" ;
    for (int i=0;i<n;i++) {
        o << sol.processingSchedule[i].getDeliveryCompletionTime() ;
        if (i<n-1) o <<  ", "; else o <<  "]" ;
    }
    o << "]" ;

    return o ;
}



